home *** CD-ROM | disk | FTP | other *** search
/ The Very Best of Atari Inside / The Very Best of Atari Inside 1.iso / mint / mint110s / tosfs.c < prev    next >
C/C++ Source or Header  |  1994-02-02  |  36KB  |  1,528 lines

  1. /*
  2. Copyright 1991,1992 Eric R. Smith.
  3. Copyright 1992,1993,1994 Atari Corporation.
  4. All rights reserved.
  5. */
  6.  
  7. /* a VERY simple tosfs.c 
  8.  * this one is extremely brain-damaged, but will serve OK for a
  9.  * skeleton in which to put a "real" tosfs.c
  10.  */
  11.  
  12. #include "mint.h"
  13.  
  14. /* if NEWWAY is defined, tosfs uses the new dup_cookie/release_cookie
  15.  * protocol to keep track of file cookies, instead of the old
  16.  * method of "timing"
  17.  */
  18. /* #define NEWWAY */
  19. #if 0
  20. #define COOKIE_DB(x) DEBUG(x)
  21. #else
  22. #define COOKIE_DB(x)
  23. #endif
  24.  
  25. /* if RO_FASCISM is defined, the read/write modes are enforced. This is
  26.  * a Good Thing, not fascist at all. Ask Allan Pratt why he chose
  27.  * that name sometime.
  28.  */
  29. #define RO_FASCISM
  30.  
  31. /* temporary code for debugging Falcon media change bug */
  32. #if 0
  33. #define MEDIA_DB(x) DEBUG(x)
  34. #else
  35. #define MEDIA_DB(x)
  36. #endif
  37.  
  38. /* search mask for anything OTHER THAN a volume label */
  39. #define FILEORDIR 0x37
  40.  
  41. char tmpbuf[PATH_MAX+1];
  42.  
  43. static long    ARGS_ON_STACK tos_root    P_((int drv, fcookie *fc));
  44. static long    ARGS_ON_STACK tos_lookup    P_((fcookie *dir, const char *name, fcookie *fc));
  45. static long    ARGS_ON_STACK tos_getxattr    P_((fcookie *fc, XATTR *xattr));
  46. static long    ARGS_ON_STACK tos_chattr    P_((fcookie *fc, int attrib));
  47. static long    ARGS_ON_STACK tos_chown    P_((fcookie *fc, int uid, int gid));
  48. static long    ARGS_ON_STACK tos_chmode    P_((fcookie *fc, unsigned mode));
  49. static long    ARGS_ON_STACK tos_mkdir    P_((fcookie *dir, const char *name, unsigned mode));
  50. static long    ARGS_ON_STACK tos_rmdir    P_((fcookie *dir, const char *name));
  51. static long    ARGS_ON_STACK tos_remove    P_((fcookie *dir, const char *name));
  52. static long    ARGS_ON_STACK tos_getname    P_((fcookie *root, fcookie *dir,
  53.                             char *pathname, int size));
  54. static long    ARGS_ON_STACK tos_rename    P_((fcookie *olddir, char *oldname,
  55.                     fcookie *newdir, const char *newname));
  56. static long    ARGS_ON_STACK tos_opendir    P_((DIR *dirh, int flags));
  57. static long    ARGS_ON_STACK tos_readdir    P_((DIR *dirh, char *nm, int nmlen, fcookie *));
  58. static long    ARGS_ON_STACK tos_rewinddir    P_((DIR *dirh));
  59. static long    ARGS_ON_STACK tos_closedir    P_((DIR *dirh));
  60. static long    ARGS_ON_STACK tos_pathconf    P_((fcookie *dir, int which));
  61. static long    ARGS_ON_STACK tos_dfree    P_((fcookie *dir, long *buf));
  62. static long    ARGS_ON_STACK tos_writelabel    P_((fcookie *dir, const char *name));
  63. static long    ARGS_ON_STACK tos_readlabel    P_((fcookie *dir, char *name, int namelen));
  64.  
  65. static long    ARGS_ON_STACK tos_creat    P_((fcookie *dir, const char *name, unsigned mode,
  66.                     int attrib, fcookie *fc));
  67. static DEVDRV *    ARGS_ON_STACK tos_getdev    P_((fcookie *fc, long *devsp));
  68. static long    ARGS_ON_STACK tos_open    P_((FILEPTR *f));
  69. static long    ARGS_ON_STACK tos_write    P_((FILEPTR *f, const char *buf, long bytes));
  70. static long    ARGS_ON_STACK tos_read    P_((FILEPTR *f, char *buf, long bytes));
  71. static long    ARGS_ON_STACK tos_lseek    P_((FILEPTR *f, long where, int whence));
  72. static long    ARGS_ON_STACK tos_ioctl    P_((FILEPTR *f, int mode, void *buf));
  73. static long    ARGS_ON_STACK tos_datime    P_((FILEPTR *f, short *time, int rwflag));
  74. static long    ARGS_ON_STACK tos_close    P_((FILEPTR *f, int pid));
  75. static long    ARGS_ON_STACK tos_dskchng    P_((int drv));
  76.  
  77. #ifdef NEWWAY
  78. static long    ARGS_ON_STACK tos_release P_((fcookie *fc));
  79. static long    ARGS_ON_STACK tos_dupcookie P_((fcookie *dst, fcookie *src));
  80. #endif
  81.  
  82. /* some routines from biosfs.c */
  83. extern long    ARGS_ON_STACK null_select    P_((FILEPTR *f, long p, int mode));
  84. extern void    ARGS_ON_STACK null_unselect    P_((FILEPTR *f, long p, int mode));
  85.  
  86. DEVDRV tos_device = {
  87.     tos_open, tos_write, tos_read, tos_lseek, tos_ioctl, tos_datime,
  88.     tos_close, null_select, null_unselect
  89. };
  90.  
  91. FILESYS tos_filesys = {
  92.     (FILESYS *)0,
  93.     FS_KNOPARSE | FS_NOXBIT | FS_LONGPATH,
  94.     tos_root,
  95.     tos_lookup, tos_creat, tos_getdev, tos_getxattr,
  96.     tos_chattr, tos_chown, tos_chmode,
  97.     tos_mkdir, tos_rmdir, tos_remove, tos_getname, tos_rename,
  98.     tos_opendir, tos_readdir, tos_rewinddir, tos_closedir,
  99.     tos_pathconf, tos_dfree, tos_writelabel, tos_readlabel,
  100.     nosymlink, noreadlink, nohardlink, nofscntl, tos_dskchng,
  101. #ifdef NEWWAY
  102.     tos_release, tos_dupcookie
  103. #else
  104.     0, 0
  105. #endif
  106. };
  107.  
  108. /* some utility functions and variables: see end of file */
  109. static DTABUF     *lastdta;    /* last DTA buffer we asked TOS about */
  110. static DTABUF    foo;
  111. static void do_setdta P_((DTABUF *dta));
  112. static int executable_extension P_((char *));
  113.  
  114. /* this array keeps track of which drives have been changed */
  115. /* a nonzero entry means that the corresponding drive has been changed,
  116.  * but GEMDOS doesn't know it yet
  117.  */
  118. static char drvchanged[NUM_DRIVES];
  119.  
  120. /* force TOS to see a media change */
  121. static void force_mediach P_((int drv));
  122. static long ARGS_ON_STACK Newgetbpb P_((int));
  123. static long ARGS_ON_STACK Newmediach P_((int));
  124. static long ARGS_ON_STACK Newrwabs P_((int, void *, int, int, int, long));
  125.  
  126. #ifdef NEWWAY
  127. #define NUM_INDICES 64
  128. #else
  129. #define NUM_INDICES 128
  130. #define MIN_AGE 8
  131. #endif
  132.  
  133. struct tindex {
  134.     char *name;        /* full path name */
  135.     FILEPTR *open;        /* fileptrs for this file; OR
  136.                  * count of number of open directories
  137.                  */
  138.     LOCK *locks;        /* locks on this file */
  139. /* file status */
  140.     long  size;
  141.     short time;
  142.     short date;
  143.     short attr;
  144.     short valid;        /* 1 if the above status is still valid */
  145. #ifdef NEWWAY
  146.     short links;        /* how many times index is in use */
  147. #else
  148.     short stamp;        /* age of this index, for garbage collection */
  149. #endif
  150. } gl_ti[NUM_INDICES];
  151.  
  152. /* temporary index for files found by readdir */
  153. static struct tindex tmpindex;
  154. static char tmpiname[PATH_MAX];
  155.  
  156. static struct tindex *tstrindex P_((char *s));
  157. static int tfullpath P_((char *result, struct tindex *base, const char *name));
  158. static struct tindex *garbage_collect P_((void));
  159.  
  160. #ifndef NEWWAY
  161. static short tclock;        /* #calls to tfullpath since last garbage
  162.                    collection */
  163. #endif
  164.  
  165. /* some extra flags for the attr field */
  166.  
  167. /*
  168.  * is a string the name of a file with executable extension?
  169.  */
  170. #define FA_EXEC 0x4000
  171. /*
  172.  * should the file be deleted when it is closed?
  173.  */
  174. #define FA_DELETE 0x2000
  175.  
  176. /*
  177.  * NOTE: call executable_extension only on a DTA name returned from
  178.  * Fsfirst(), not on an arbitrary path, for two reasons: (1) it
  179.  * expects only upper case, and (2) it looks only for the 1st extension,
  180.  * so a folder with a '.' in its name would confuse it.
  181.  */
  182.  
  183. static int
  184. executable_extension(s)
  185.     char *s;
  186. {
  187.     while (*s && *s != '.') s++;
  188.     if (!*s) return 0;
  189.     s++;
  190.     if (s[0] == 'T') {
  191.         return (s[1] == 'T' && s[2] == 'P') ||
  192.                (s[1] == 'O' && s[2] == 'S');
  193.     }
  194.     if (s[0] == 'P')
  195.         return s[1] == 'R' && s[2] == 'G';
  196.     if (s[0] == 'A')
  197.         return s[1] == 'P' && s[2] == 'P';
  198.     if (s[0] == 'G')
  199.         return s[1] == 'T' && s[2] == 'P';
  200.     return 0;
  201. }
  202.  
  203. /*
  204.  * Look in the table of tos indices to see if an index corresponding
  205.  * to this file name already exists. If so, mark it as being used
  206.  * and return it. If not, find an empty slot and make an index for
  207.  * this string. If no empty slots exist, garbage collect and
  208.  * try again.
  209.  *
  210.  * This routine is pretty dumb; we really should use a hash table
  211.  * of some sort
  212.  */
  213.  
  214. static struct tindex *tstrindex(s)
  215.     char *s;
  216. {
  217.     int i;
  218.     char *r;
  219.     struct tindex *t, *free = 0;
  220.  
  221.     assert(s != 0);
  222.     t = gl_ti;
  223.     for (i = 0; i < NUM_INDICES; i++, t++) {
  224.         if (t->name && !stricmp(t->name, s)) {
  225. #ifndef NEWWAY
  226.             t->stamp = tclock;    /* update use time */
  227. #endif
  228.             return t;
  229.         }
  230.         else if (!t->name && !free)
  231.             free = t;
  232.     }
  233.     if (!free) {
  234.         free = garbage_collect();
  235.     }
  236. #ifdef NEWWAY
  237.     if (!free) {
  238.         FORCE("tosfs: all slots in use!!");
  239.         FORCE("Links\tName");
  240.         t = gl_ti;
  241.         for (i = 0; i < NUM_INDICES; i++,t++) {
  242.             FORCE("%d\t%s", t->links, t->name);
  243.         }
  244.         FATAL("tosfs: unable to get a file name index");
  245.     }
  246. #else
  247.     if (!free) {
  248.         FATAL("tosfs: unable to get a file name index");
  249.     }
  250. #endif
  251.     r = kmalloc((long)strlen(s)+1);
  252.     if (!r) {
  253.         FATAL("tosfs: unable to allocate space for a file name");
  254.     }
  255.     strcpy(r, s);
  256.     free->name = r;
  257. #ifdef NEWWAY
  258.     free->links = 0;
  259. #else
  260.     free->stamp = tclock;
  261. #endif
  262.     free->open = 0;
  263.     free->locks = 0;
  264.  
  265. /* check to see if this file was recently returned by opendir() */
  266. #ifndef NEWWAY
  267.     if (tmpindex.valid && tclock - tmpindex.stamp < MIN_AGE &&
  268.         !stricmp(free->name, tmpindex.name)) {
  269.         free->size = tmpindex.size;
  270.         free->time = tmpindex.time;
  271.         free->date = tmpindex.date;
  272.         free->attr = tmpindex.attr;
  273.         free->valid = 1;
  274.         tmpindex.valid = 0;
  275.     } else
  276. #endif
  277.         free->valid = 0;
  278.     return free;
  279. }
  280.  
  281. /*
  282.  * garbage collection routine: for any TOS index older than MIN_AGE,
  283.  * check through all current processes to see if it's in use. If
  284.  * not, free the corresponding string.
  285.  * Returns: a pointer to a newly freed index, or NULL.
  286.  */
  287.  
  288. /* it's unlikely that the kernel would need to hold onto a file cookie
  289.    for longer than this many calls to tstrindex() without first
  290.    saving the cookie in a directory or file pointer
  291.  */
  292.  
  293. static struct tindex *
  294. garbage_collect()
  295. {
  296.     struct tindex *free, *t;
  297.     int i;
  298. #ifndef NEWWAY
  299.     fcookie *fc, *gc;
  300.     PROC *p;
  301.     int j;
  302.     int age;
  303. #endif
  304.  
  305.     free = 0;
  306.     t = gl_ti;
  307.     for (i = 0; i < NUM_INDICES; i++,t++) {
  308.         if (!t->name) continue;
  309. #ifdef NEWWAY
  310.         if (t->links == 0) {
  311.             kfree(t->name);
  312.             t->name = 0;
  313.             if (!free) free = t;
  314.         }
  315. #else
  316.         age = tclock - t->stamp;
  317.         t->stamp = 0;
  318.         assert(age >= 0);
  319.         if (age > MIN_AGE) {
  320.         /* see if any process is using this index */
  321.             if (t->open)
  322.                 goto found_index;
  323.             for (p = proclist; p; p = p->gl_next) {
  324.                 fc = p->curdir;
  325.                 gc = p->root;
  326.                 for (j = 0; j < NUM_DRIVES; j++,fc++,gc++) {
  327.                     if (( fc->fs == &tos_filesys &&
  328.                           fc->index == (long)t ) ||
  329.                         ( gc->fs == &tos_filesys &&
  330.                           gc->index == (long)t ) )
  331.                         goto found_index;
  332.                 }
  333.             }
  334.         /* here, we couldn't find the index in use by any proc. */
  335.             kfree(t->name);
  336.             t->name = 0;
  337.             if (!free)
  338.                 free = t;
  339.         found_index:
  340.             ;
  341.         } else {
  342.     /* make sure that future garbage collections might look at this file */
  343.             t->stamp = -age;
  344.         }
  345. #endif
  346.     }
  347.  
  348. #ifndef NEWWAY
  349.     tclock = 0;    /* reset the clock */
  350.     tmpindex.valid = 0; /* expire the temporary Fsfirst buffer */
  351. #endif
  352.     return free;
  353. }
  354.  
  355. #define DIRSEP(c) ((c) == '\\')
  356.  
  357. static int
  358. tfullpath(result, basei, path)
  359.     char *result;
  360.     struct tindex *basei;
  361.     const char *path;
  362. {
  363. #define TNMTEMP 32
  364.     char *n, name[TNMTEMP+1];
  365.     int namelen, pathlen;
  366.     char *base = basei->name;
  367.     int r = 0;
  368.  
  369. #ifndef NEWWAY
  370.     basei->stamp = ++tclock;
  371.     if (tclock > 10000) {
  372.     /* garbage collect every so often whether we need it or not */
  373.         (void)garbage_collect();
  374.     }
  375. #endif
  376.     if (!*path) {
  377.         strncpy(result, base, PATH_MAX-1);
  378.         return r;
  379.     }
  380.  
  381.     strncpy(result, base, PATH_MAX-1);
  382.  
  383.     pathlen = strlen(result);
  384.  
  385. /* now path is relative to what's currently in "result" */
  386.  
  387.     while(*path) {
  388. /* get next name in path */
  389.         n = name; namelen = 0;
  390.         while (*path && !DIRSEP(*path)) {
  391. /* BUG: we really should to the translation to DOS 8.3
  392.  * format *here*, so that really long names are truncated
  393.  * correctly.
  394.  */
  395.             if (namelen < TNMTEMP) {
  396.                 *n++ = toupper(*path); path++; namelen++;
  397.             }
  398.             else
  399.                 path++;
  400.         }
  401.         *n = 0;
  402.         while (DIRSEP(*path)) path++;
  403. /* check for "." and ".." */
  404.         if (!strcmp(name, ".")) continue;
  405.         if (!strcmp(name, "..")) {
  406.             n = strrchr(result, '\\');
  407.             if (n) {
  408.                 *n = 0;
  409.                 pathlen = (int)(n - result);
  410.             }
  411.             else r = EMOUNT;
  412.             continue;
  413.         }
  414.         if (pathlen + namelen < PATH_MAX - 1) {
  415.             strcat(result, "\\");
  416.             pathlen++;
  417.  
  418.     /* make sure the name is restricted to DOS 8.3 format */
  419.             for (base = result; *base; base++)
  420.                 ;
  421.             n = name;
  422.             namelen = 0;
  423.             while (*n && *n != '.' && namelen++ < 8) {
  424.                 *base++ = *n++;
  425.                 pathlen++;
  426.             }
  427.             while (*n && *n != '.') n++;
  428.             if (*n == '.' && *(n+1) != 0) {
  429.                 *base++ = *n++;
  430.                 pathlen++;
  431.                 namelen = 0;
  432.                 while (*n && namelen++ < 3) {
  433.                     *base++ = *n++;
  434.                     pathlen++;
  435.                 }
  436.             }
  437.             *base = 0;
  438.         }
  439.     }
  440.     return r;
  441. }
  442.  
  443. static long ARGS_ON_STACK 
  444. tos_root(drv, fc)
  445.     int drv;
  446.     fcookie *fc;
  447. {
  448.     struct tindex *ti;
  449.  
  450.     ksprintf(tmpbuf, "%c:", drv+'A');
  451.     fc->fs = &tos_filesys;
  452.     fc->dev = drv;
  453.     ti = tstrindex(tmpbuf);
  454.     ti->size = ti->date = ti->time = 0;
  455.     ti->attr = FA_DIR;
  456.     ti->valid = 1;
  457.     fc->index = (long)ti;
  458.  
  459. /* if the drive has changed, make sure GEMDOS knows it! */
  460.     if (drvchanged[drv]) {
  461.         force_mediach(drv);
  462.     }
  463. #ifdef NEWWAY
  464.     ti->links++;
  465. #endif
  466.     return 0;
  467. }
  468.  
  469. static long ARGS_ON_STACK 
  470. tos_lookup(dir, name, fc)
  471.     fcookie *dir;
  472.     const char *name;
  473.     fcookie *fc;
  474. {
  475.     long r;
  476.     struct tindex *ti = (struct tindex *)dir->index;
  477.  
  478.     r = tfullpath(tmpbuf, ti, name);
  479.  
  480. /* if the name is empty or otherwise trivial, just return the directory */
  481.     if (!strcmp(ti->name, tmpbuf)) {
  482.         *fc = *dir;
  483. #ifdef NEWWAY
  484.         ti->links++;
  485.         COOKIE_DB(("tos_lookup: %s now has %d links", ti->name, ti->links));
  486. #endif 
  487.         return r;
  488.     }
  489.  
  490. /* is there already an index for this file?? If so, is it up to date?? */
  491.     ti = tstrindex(tmpbuf);
  492.     if (!ti->valid) {
  493.         if (tmpbuf[1] == ':' && tmpbuf[2] == 0) {
  494.             /* a root directory -- lookup always succeeds */
  495.             foo.dta_size = 0;
  496.             foo.dta_date = foo.dta_time = 0;
  497.             foo.dta_attrib = FA_DIR;
  498.             foo.dta_name[0] = 0;
  499.         } else {
  500.             do_setdta(&foo);
  501.             r = Fsfirst(tmpbuf, FILEORDIR);
  502.             if (r) {
  503. DEBUG(("tos_lookup: Fsfirst(%s) returned %ld", tmpbuf, r));
  504.                 return r;
  505.             }
  506.         }
  507.         ti->size = foo.dta_size;
  508.         ti->date = foo.dta_date;
  509.         ti->time = foo.dta_time;
  510.         ti->attr = foo.dta_attrib;
  511.         if (executable_extension(foo.dta_name))
  512.             ti->attr |= FA_EXEC;
  513.         ti->valid = 1;
  514.     }
  515.     fc->fs = &tos_filesys;
  516.     fc->index = (long)ti;
  517.     fc->dev = dir->dev;
  518. #ifdef NEWWAY
  519.     ti->links++;
  520.     COOKIE_DB(("tos_lookup: %s now has %d links", ti->name, ti->links));
  521. #endif
  522.     return r;
  523. }
  524.  
  525. static long ARGS_ON_STACK 
  526. tos_getxattr(fc, xattr)
  527.     fcookie *fc;
  528.     XATTR *xattr;
  529. {
  530.     struct tindex *ti = (struct tindex *)fc->index;
  531.     long r;
  532.     static long junkindex = 0;
  533.  
  534.     xattr->index = junkindex++;
  535.     xattr->dev = fc->dev;
  536.     xattr->rdev = fc->dev;
  537.     xattr->nlink = 1;
  538.     xattr->uid = xattr->gid = 0;
  539.  
  540. #ifndef NEWWAY
  541.     ti->stamp = ++tclock;
  542. #endif
  543.  
  544.     if (!ti->valid) {
  545.         do_setdta(&foo);
  546.         if (ti->name[2] == 0) {        /* a root directory */
  547. /* actually, this can also happen if a program tries to open a file
  548.  * with an empty name... so we should fail gracefully
  549.  */
  550.             ti->attr = FA_DIR;
  551.             ti->size = 0;
  552.             ti->date = ti->time = 0;
  553.             goto around;
  554.         }
  555.     
  556.         r = Fsfirst(ti->name, FILEORDIR);
  557.         if (r) {
  558.             DEBUG(("tosfs: search error %ld on [%s]", r, ti->name));
  559.             return r;
  560.         }
  561.         ti->size = foo.dta_size;
  562.         ti->date = foo.dta_date;
  563.         ti->time = foo.dta_time;
  564.         ti->attr = foo.dta_attrib;
  565.         if (executable_extension(foo.dta_name))
  566.             ti->attr |= FA_EXEC;
  567. around:
  568.         ti->valid = 1;
  569.     }
  570.     xattr->size = ti->size;
  571.  
  572. /* BUG: blksize isn't accurate if the sector size is not 512 */
  573.     xattr->blksize = 1024;
  574.     xattr->nblocks = (xattr->size + 1023) / 1024;
  575.     xattr->mdate = xattr->cdate = xattr->adate = ti->date;
  576.     xattr->mtime = xattr->ctime = xattr->atime = ti->time;
  577.     xattr->mode = (ti->attr & FA_DIR) ? (S_IFDIR | DEFAULT_DIRMODE) :
  578.              (S_IFREG | DEFAULT_MODE);
  579.  
  580. /* TOS files have permissions rwxrwx--- */
  581.     xattr->mode &= ~(S_IROTH|S_IWOTH|S_IXOTH);
  582.  
  583.     if (ti->attr & FA_RDONLY) {
  584.         xattr->mode &= ~(S_IWUSR|S_IWGRP|S_IWOTH);
  585.     }
  586.  
  587.     if (ti->attr & FA_EXEC) {
  588.         xattr->mode |= (S_IXUSR|S_IXGRP|S_IXOTH);
  589.     }
  590.     xattr->attr = ti->attr & 0xff;
  591.     return 0;
  592. }
  593.  
  594. static long ARGS_ON_STACK
  595. tos_chattr(fc, attrib)
  596.     fcookie *fc;
  597.     int attrib;
  598. {
  599.     struct tindex *ti = (struct tindex *)fc->index;
  600.  
  601.     if (ti->attr & FA_DIR) {
  602.         DEBUG(("error: attempt to change attributes of a directory"));
  603.         return EACCDN;
  604.     }
  605.     ti->valid = 0;
  606.     (void)tfullpath(tmpbuf, ti, "");
  607.     return Fattrib(tmpbuf, 1, attrib);
  608. }
  609.  
  610. static long ARGS_ON_STACK 
  611. tos_chown(dir, uid, gid)
  612.     fcookie *dir;
  613.     int uid, gid;
  614. {
  615.     UNUSED(dir); UNUSED(uid); UNUSED(gid);
  616.     return EINVFN;
  617. }
  618.  
  619. static long ARGS_ON_STACK 
  620. tos_chmode(fc, mode)
  621.     fcookie *fc;
  622.     unsigned mode;
  623. {
  624.     int oldattr, newattr;
  625.     long r;
  626.     struct tindex *ti = (struct tindex *)fc->index;
  627.  
  628.     oldattr = Fattrib(ti->name, 0, 0);
  629.     if (oldattr < 0)
  630.         return oldattr;
  631.  
  632.     ti->valid = 0;
  633.  
  634.     if (!(mode & S_IWUSR))
  635.         newattr = oldattr | FA_RDONLY;
  636.     else
  637.         newattr = oldattr & ~FA_RDONLY;
  638.     if (newattr != oldattr)
  639.         r = Fattrib(ti->name, 1, newattr);
  640.     else
  641.         r = 0;
  642.     return (r < 0) ? r : 0;
  643. }
  644.  
  645. static long ARGS_ON_STACK 
  646. tos_mkdir(dir, name, mode)
  647.     fcookie *dir;
  648.     const char *name;
  649.     unsigned mode;        /* ignored under TOS */
  650. {
  651.     UNUSED(mode);
  652.  
  653.     (void)tfullpath(tmpbuf, (struct tindex *)dir->index, name);
  654.     tmpindex.valid = 0;
  655.  
  656.     return Dcreate(tmpbuf);
  657. }
  658.  
  659. static long ARGS_ON_STACK 
  660. tos_rmdir(dir, name)
  661.     fcookie *dir;
  662.     const char *name;
  663. {
  664.     struct tindex *ti;
  665.  
  666.     (void)tfullpath(tmpbuf, (struct tindex *)dir->index, name);
  667.     ti = tstrindex(tmpbuf);
  668.     ti->valid = 0;
  669.  
  670.     return Ddelete(tmpbuf);
  671. }
  672.  
  673. static long ARGS_ON_STACK 
  674. tos_remove(dir, name)
  675.     fcookie *dir;
  676.     const char *name;
  677. {
  678.     struct tindex *ti;
  679.  
  680.     (void)tfullpath(tmpbuf, (struct tindex *)dir->index, name);
  681.  
  682.     ti = tstrindex(tmpbuf);
  683.     if (ti->open) {
  684.         DEBUG(("tos_remove: file is open, will be deleted later"));
  685.         if (ti->attr & FA_RDONLY)
  686.             return EACCDN;
  687.         ti->attr |= FA_DELETE;
  688.         return 0;
  689.     }
  690.     ti->valid = 0;
  691.     return Fdelete(tmpbuf);
  692. }
  693.  
  694. static long ARGS_ON_STACK 
  695. tos_getname(root, dir, pathname, size)
  696.     fcookie *root, *dir;
  697.     char *pathname;
  698.     int size;
  699. {
  700.     char *rootnam = ((struct tindex *)root->index)->name;
  701.     char *dirnam = ((struct tindex *)dir->index)->name;
  702.     int i;
  703.  
  704.     i = strlen(rootnam);
  705.     if (strlen(dirnam) < i) {
  706.         DEBUG(("tos_getname: root is longer than path"));
  707.         return EINTRN;
  708.     }
  709.     if (strlen(dirnam+i) < size) {
  710.         strcpy(pathname, dirnam + i);
  711. /*
  712.  * BUG: must be a better way to decide upper/lower case
  713.  */
  714.         if (curproc->domain == DOM_MINT)
  715.             strlwr(pathname);
  716.         return 0;
  717.     } else {
  718.         DEBUG(("tosfs: name too long"));
  719.         return ERANGE;
  720.     }
  721. }
  722.  
  723. static long ARGS_ON_STACK 
  724. tos_rename(olddir, oldname, newdir, newname)
  725.     fcookie *olddir;
  726.     char *oldname;
  727.     fcookie *newdir;
  728.     const char *newname;
  729. {
  730.     char newbuf[128];
  731.     struct tindex *ti;
  732.     long r;
  733.  
  734.     (void)tfullpath(tmpbuf, (struct tindex *)olddir->index, oldname);
  735.     (void)tfullpath(newbuf, (struct tindex *)newdir->index, newname);
  736.     r = Frename(0, tmpbuf, newbuf);
  737.     if (r == 0) {
  738.         ti = tstrindex(tmpbuf);
  739.         kfree(ti->name);
  740.         ti->name = kmalloc((long)strlen(newbuf)+1);
  741.         if (!ti->name) {
  742.             FATAL("tosfs: unable to allocate space for a name");
  743.         }
  744.         strcpy(ti->name, newbuf);
  745.         ti->valid = 0;
  746.     }
  747.     return r;
  748. }
  749.  
  750. #define DIR_FLAG(x)    (x->fsstuff[0])
  751. #define STARTSEARCH    0    /* opendir() was just called */
  752. #define INSEARCH    1    /* readdir() has been called at least once */
  753. #define NMFILE        2    /* no more files to read */
  754.  
  755. #define DIR_DTA(x)    ((DTABUF *)(x->fsstuff + 2))
  756. #define DIR_NAME(x)    (x->fsstuff + 32)
  757.  
  758. /*
  759.  * The directory functions are a bit tricky. What we do is have
  760.  * opendir() do Fsfirst; the first readdir() picks up this name,
  761.  * subsequent readdir()'s have to do Fsnext
  762.  */
  763.  
  764. static long ARGS_ON_STACK 
  765. tos_opendir(dirh, flags)
  766.     DIR *dirh;
  767.     int flags;
  768. {
  769.     long r;
  770.     struct tindex *t = (struct tindex *)dirh->fc.index;
  771.  
  772.     UNUSED(flags);
  773.  
  774.     (void)tfullpath(tmpbuf, t, "*.*");
  775.  
  776.     do_setdta(DIR_DTA(dirh));
  777.  
  778.     r = Fsfirst(tmpbuf, FILEORDIR);
  779.  
  780.     if (r == 0) {
  781.         t->open++;
  782.         DIR_FLAG(dirh) = STARTSEARCH;
  783.         return 0;
  784.     } else if (r == EFILNF) {
  785.         t->open++;
  786.         DIR_FLAG(dirh) = NMFILE;
  787.         return 0;
  788.     }
  789.      return r;
  790. }
  791.  
  792. static long ARGS_ON_STACK 
  793. tos_readdir(dirh, name, namelen, fc)
  794.     DIR *dirh;
  795.     char *name;
  796.     int namelen;
  797.     fcookie *fc;
  798. {
  799.     static long index = 0;
  800.     long ret;
  801.     int giveindex = dirh->flags == 0;
  802.     struct tindex *ti;
  803.     DTABUF *dta = DIR_DTA(dirh);
  804.  
  805. again:
  806.     if (DIR_FLAG(dirh) == NMFILE)
  807.         return ENMFIL;
  808.  
  809.     if (DIR_FLAG(dirh) == STARTSEARCH) {
  810.         DIR_FLAG(dirh) = INSEARCH;
  811.     } else {
  812.         assert(DIR_FLAG(dirh) == INSEARCH);
  813.         do_setdta(dta);
  814.         ret = Fsnext();
  815.         if (ret) {
  816.             DIR_FLAG(dirh) = NMFILE;
  817.             return ret;
  818.         }
  819.     }
  820.  
  821. /* don't return volume labels from readdir */
  822.     if (dta->dta_attrib == FA_LABEL) goto again;
  823.  
  824.     fc->fs = &tos_filesys;
  825.     fc->dev = dirh->fc.dev;
  826.  
  827.     (void)tfullpath(tmpiname, (struct tindex *)dirh->fc.index, DIR_NAME(dirh));
  828.  
  829.     ti = &tmpindex;
  830.     ti->name = tmpiname;
  831.     ti->valid = 1;
  832.     ti->size = dta->dta_size;
  833.     ti->date = dta->dta_date;
  834.     ti->time = dta->dta_time;
  835.     ti->attr = dta->dta_attrib;
  836. #ifndef NEWWAY
  837.     ti->stamp = tclock;
  838. #endif
  839.     if (executable_extension(dta->dta_name))
  840.         ti->attr |= FA_EXEC;
  841.     fc->index = (long)ti;
  842.  
  843.     if (giveindex) {
  844.         namelen -= (int) sizeof(long);
  845.         if (namelen <= 0) return ERANGE;
  846.         *((long *)name) = index++;
  847.         name += sizeof(long);
  848.     }
  849.     strncpy(name, DIR_NAME(dirh), namelen-1);
  850.     name[namelen-1] = 0;
  851.  
  852. /* BUG: we really should do the "strlwr" operation only
  853.  * for Dreaddir (i.e. if giveindex == 0) but
  854.  * unfortunately some old programs rely on the behaviour
  855.  * below
  856.  */
  857.     if (curproc->domain == DOM_MINT) {
  858.         strlwr(name);
  859.     }
  860.     if (strlen(DIR_NAME(dirh)) >= namelen)
  861.         return ENAMETOOLONG;
  862. #ifdef NEWWAY
  863.     ti->links++;
  864.     COOKIE_DB(("tos_readdir: %s now has %d links", ti->name, ti->links));
  865. #endif
  866.     return 0;
  867. }
  868.  
  869. static long ARGS_ON_STACK 
  870. tos_rewinddir(dirh)
  871.     DIR *dirh;
  872. {
  873.     struct tindex *ti = (struct tindex *)dirh->fc.index;
  874.     long r;
  875.  
  876.     (void)tfullpath(tmpbuf, ti, "*.*");
  877.     do_setdta(DIR_DTA(dirh));
  878.     r = Fsfirst(tmpbuf, FILEORDIR);
  879.     if (r == 0) {
  880.         DIR_FLAG(dirh) = STARTSEARCH;
  881.     } else {
  882.         DIR_FLAG(dirh) = NMFILE;
  883.     }
  884.     return r;
  885. }
  886.  
  887. static long ARGS_ON_STACK 
  888. tos_closedir(dirh)
  889.     DIR *dirh;
  890. {
  891.     struct tindex *t = (struct tindex *)dirh->fc.index;
  892.  
  893.     if (t->open == 0) {
  894.         FATAL("t->open == 0: directory == %s", t->name);
  895.     }
  896.     --t->open;
  897.     DIR_FLAG(dirh) = NMFILE;
  898.     return 0;
  899. }
  900.  
  901. static long ARGS_ON_STACK 
  902. tos_pathconf(dir, which)
  903.     fcookie *dir;
  904.     int which;
  905. {
  906.     UNUSED(dir);
  907.  
  908.     switch(which) {
  909.     case -1:
  910.         return DP_MAXREQ;
  911.     case DP_IOPEN:
  912.         return 60;    /* we can only keep about this many open */
  913.     case DP_MAXLINKS:
  914.          return 1;    /* no hard links */
  915.     case DP_PATHMAX:
  916.         return PATH_MAX;
  917.     case DP_NAMEMAX:
  918.         return 8+3+1;
  919.     case DP_ATOMIC:
  920.         return 512;    /* we can write at least a sector atomically */
  921.     case DP_TRUNC:
  922.         return DP_DOSTRUNC;    /* DOS style file names */
  923.     case DP_CASE:
  924.         return DP_CASECONV;    /* names converted to upper case */
  925.     default:
  926.         return EINVFN;
  927.     }
  928. }
  929.  
  930. long ARGS_ON_STACK 
  931. tos_dfree(dir, buf)
  932.     fcookie *dir;
  933.     long *buf;
  934. {
  935.     return Dfree(buf, (dir->dev)+1);
  936. }
  937.  
  938. /*
  939.  * writelabel: creates a volume label
  940.  * readlabel: reads a volume label
  941.  * both of these are only guaranteed to work in the root directory
  942.  */
  943.  
  944. /*
  945.  * BUG: this should first delete any old labels, so that it will
  946.  * work with TOS <1.4
  947.  */
  948.  
  949. long ARGS_ON_STACK 
  950. tos_writelabel(dir, name)
  951.     fcookie *dir;
  952.     const char *name;
  953. {
  954.     long r;
  955.     struct tindex *ti;
  956.  
  957.     (void)tfullpath(tmpbuf, (struct tindex *)dir->index, name);
  958.     r = Fcreate(tmpbuf, FA_LABEL);
  959.     if (r < 0) return r;
  960.     (void)Fclose((int)r);
  961.     ti = tstrindex(tmpbuf);
  962.     ti->valid = 0;
  963.     return 0;
  964. }
  965.  
  966. long ARGS_ON_STACK 
  967. tos_readlabel(dir, name, namelen)
  968.     fcookie *dir;
  969.     char *name;
  970.     int namelen;
  971. {
  972.     long r;
  973.     struct tindex *ti = (struct tindex *)dir->index;
  974.  
  975.     if (ti->name[2] != 0)        /* not a root directory? */
  976.         return EFILNF;
  977.  
  978.     (void)tfullpath(tmpbuf, ti, "*.*");
  979.     do_setdta(&foo);
  980.     r = Fsfirst(tmpbuf, FA_LABEL);
  981.     if (r)
  982.         return r;
  983.     strncpy(name, foo.dta_name, namelen-1);
  984.     return (strlen(foo.dta_name) < namelen) ? 0 : ENAMETOOLONG;
  985. }
  986.  
  987. /*
  988.  * TOS creat: this doesn't actually create the file, rather it
  989.  * sets up a (fake) index for the file that will be used by
  990.  * the later tos_open call.
  991.  */
  992.  
  993. static long ARGS_ON_STACK 
  994. tos_creat(dir, name, mode, attrib, fc)
  995.     fcookie *dir;
  996.     const char *name;
  997.     unsigned mode;
  998.     int attrib;
  999.     fcookie *fc;
  1000. {
  1001.     struct tindex *ti;
  1002.     UNUSED(mode);
  1003.  
  1004.     (void)tfullpath(tmpbuf, (struct tindex *)dir->index, name);
  1005.  
  1006.     ti = tstrindex(tmpbuf);
  1007.     ti->size = 0;
  1008.     ti->date = datestamp;
  1009.     ti->time = timestamp;
  1010.     ti->attr = attrib;
  1011.     ti->valid = 1;
  1012.  
  1013.     fc->fs = &tos_filesys;
  1014.     fc->index = (long)ti;
  1015.     fc->dev = dir->dev;
  1016. #ifdef NEWWAY
  1017.     ti->links++;
  1018.     COOKIE_DB(("tos_creat: %s now has %d links", ti->name, ti->links));
  1019. #endif
  1020.     return 0;
  1021. }
  1022.     
  1023. /*
  1024.  * TOS device driver
  1025.  */
  1026.  
  1027. static DEVDRV * ARGS_ON_STACK 
  1028. tos_getdev(fc, devsp)
  1029.     fcookie *fc;
  1030.     long *devsp;
  1031. {
  1032.     UNUSED(fc); UNUSED(devsp);
  1033.     return &tos_device;
  1034. }
  1035.  
  1036. static long ARGS_ON_STACK 
  1037. tos_open(f)
  1038.     FILEPTR *f;
  1039. {
  1040.     struct tindex *ti;
  1041.     int mode = f->flags;
  1042.     int tosmode;
  1043.     long r;
  1044.     extern int flk;        /* in main.c, set if _FLK cookie already present */
  1045.  
  1046.     ti = (struct tindex *)(f->fc.index);
  1047.     assert(ti != 0);
  1048.  
  1049. #ifndef NEWWAY
  1050.     ti->stamp = ++tclock;
  1051. #endif
  1052.     ti->valid = 0;
  1053.  
  1054. #ifndef RO_FASCISM
  1055. /* TEMPORARY HACK: change all modes to O_RDWR for files opened in
  1056.  * compatibility sharing mode. This is silly, but
  1057.  * allows broken TOS programs that write to read-only handles to continue
  1058.  * to work (it also helps file sharing, by making the realistic assumption
  1059.  * that any open TOS file can be written to). Eventually,
  1060.  * this should be tuneable by the user somehow.
  1061.  * ALSO: change O_COMPAT opens into O_DENYNONE; again, this may be temporary.
  1062.  */
  1063.     if ( (mode & O_SHMODE) == O_COMPAT ) {
  1064.         f->flags = (mode & ~(O_RWMODE|O_SHMODE)) | O_RDWR | O_DENYNONE;
  1065.     }
  1066. #endif
  1067.  
  1068. /* check to see that nobody has opened this file already in an
  1069.  * incompatible mode
  1070.  */
  1071.     if (denyshare(ti->open, f)) {
  1072.         TRACE(("tos_open: file sharing denied"));
  1073.         return EACCDN;
  1074.     }
  1075.  
  1076. /*
  1077.  * now open the file; if O_TRUNC was specified, actually
  1078.  * create the file anew.
  1079.  * BUG: O_TRUNC without O_CREAT doesn't work right. The kernel doesn't
  1080.  * use this mode, anyways
  1081.  */
  1082.  
  1083.     if (mode & O_TRUNC) {
  1084.         if (ti->open) {
  1085.             DEBUG(("tos_open: attempt to truncate an open file"));
  1086.             return EACCDN;
  1087.         }
  1088.         r = Fcreate(ti->name, ti->attr);
  1089.     } else {
  1090.         if (flk)
  1091.             tosmode = mode & (O_RWMODE|O_SHMODE);
  1092.         else
  1093.             tosmode = (mode & O_RWMODE);
  1094.         if (tosmode == O_EXEC) tosmode = O_RDONLY;
  1095.  
  1096.         r = Fopen(ti->name, tosmode );
  1097.         if (r == EFILNF && (mode & O_CREAT))
  1098.             r = Fcreate(ti->name, ti->attr);
  1099.     }
  1100.  
  1101.     if (r < 0) {
  1102. /* get rid of the index for the file, since it doesn't exist */
  1103.         kfree(ti->name);
  1104.         ti->name = 0;
  1105.         ti->valid = 0;
  1106.         return r;
  1107.     }
  1108.  
  1109.     f->devinfo = r;
  1110.  
  1111.     f->next = ti->open;
  1112.     ti->open = f;
  1113.     return 0;
  1114. }
  1115.  
  1116. static long ARGS_ON_STACK 
  1117. tos_write(f, buf, bytes)
  1118.     FILEPTR *f; const char *buf; long bytes;
  1119. {
  1120.     struct tindex *ti = (struct tindex *)f->fc.index;
  1121.  
  1122.     ti->valid = 0;
  1123.     return Fwrite((int)f->devinfo, bytes, buf);
  1124. }
  1125.  
  1126. static long ARGS_ON_STACK 
  1127. tos_read(f, buf, bytes)
  1128.     FILEPTR *f; char *buf; long bytes;
  1129. {
  1130.     return Fread((int)f->devinfo, bytes, buf);
  1131. }
  1132.  
  1133. static long ARGS_ON_STACK 
  1134. tos_lseek(f, where, whence)
  1135.     FILEPTR *f; long where; int whence;
  1136. {
  1137.     long r;
  1138.  
  1139.     r = Fseek(where, (int)f->devinfo, whence);
  1140.     return r;
  1141. }
  1142.  
  1143. static long ARGS_ON_STACK 
  1144. tos_ioctl(f, mode, buf)
  1145.     FILEPTR *f; int mode; void *buf;
  1146. {
  1147.     LOCK t, *lck, **old;
  1148.     struct flock *fl;
  1149.     long r;
  1150.     struct tindex *ti;
  1151.     extern int flk;        /* set in main.c if _FLK already installed */
  1152.  
  1153.     if (mode == FIONREAD || mode == FIONWRITE) {
  1154.         *((long *)buf) = 1;
  1155.         return 0;
  1156.     }
  1157.     else if (mode == F_SETLK || mode == F_SETLKW || mode == F_GETLK) {
  1158.         fl = ((struct flock *)buf);
  1159.         t.l = *fl;
  1160.         switch(t.l.l_whence) {
  1161.         case 0:
  1162.             break;
  1163.         case 1:        /* SEEK_CUR */
  1164.             r = Fseek(0L, (int)f->devinfo, 1);
  1165.             t.l.l_start += r;
  1166.             break;
  1167.         case 2:
  1168.             r = Fseek(0L, (int)f->devinfo, 1);
  1169.             t.l.l_start = Fseek(t.l.l_start, (int)f->devinfo, 2);
  1170.             (void)Fseek(r, (int)f->devinfo, 0);
  1171.             break;
  1172.         default:
  1173.             DEBUG(("Invalid value for l_whence"));
  1174.             return EINVFN;
  1175.         }
  1176. /* BUG: can't lock a file starting at >2gigabytes from the beginning */
  1177.         if (t.l.l_start < 0) t.l.l_start = 0;
  1178.         t.l.l_whence = 0;
  1179.         ti = (struct tindex *)f->fc.index;
  1180.  
  1181.         if (mode == F_GETLK) {
  1182.             lck = denylock(ti->locks, &t);
  1183.             if (lck)
  1184.                 *fl = lck->l;
  1185.             else
  1186.                 fl->l_type = F_UNLCK;
  1187.             return 0;
  1188.         }
  1189.  
  1190.         if (t.l.l_type == F_UNLCK) {
  1191.         /* try to find the lock */
  1192.             old = &ti->locks;
  1193.             lck = *old;
  1194.             while (lck) {
  1195.                 if (lck->l.l_pid == curproc->pid &&
  1196.                     lck->l.l_start == t.l.l_start &&
  1197.                     lck->l.l_len == t.l.l_len) {
  1198.         /* found it -- remove the lock */
  1199.                     *old = lck->next;
  1200.                     TRACE(("tosfs: unlocked %s: %ld + %ld",
  1201.                         ti->name, t.l.l_start, t.l.l_len));
  1202.                     if (flk)
  1203.                         (void)Flock((int)f->devinfo, 1,
  1204.                             t.l.l_start, t.l.l_len);
  1205.                 /* wake up anyone waiting on the lock */
  1206.                     wake(IO_Q, (long)lck);
  1207.                     kfree(lck);
  1208.                     break;
  1209.                 }
  1210.                 old = &lck->next;
  1211.                 lck = lck->next;
  1212.             }
  1213.             return lck ?  0 : ENSLOCK;
  1214.         }
  1215.         TRACE(("tosfs: lock %s: %ld + %ld", ti->name,
  1216.             t.l.l_start, t.l.l_len));
  1217.         do {
  1218.         /* see if there's a conflicting lock */
  1219.             while ((lck = denylock(ti->locks, &t)) != 0) {
  1220.                 DEBUG(("tosfs: lock conflicts with one held by %d",
  1221.                     lck->l.l_pid));
  1222.                 if (mode == F_SETLKW) {
  1223.                     sleep(IO_Q, (long)lck);        /* sleep a while */
  1224.                 }
  1225.                 else
  1226.                     return ELOCKED;
  1227.             }
  1228.         /* if not, add this lock to the list */
  1229.             lck = kmalloc(SIZEOF(LOCK));
  1230.             if (!lck) return ENSMEM;
  1231.         /* see if other _FLK code might object */
  1232.             if (flk) {
  1233.                 r = Flock((int)f->devinfo, 0, t.l.l_start, t.l.l_len);
  1234.                 if (r) {
  1235.                     kfree(lck);
  1236.                     if (mode == F_SETLKW && r == ELOCKED) {
  1237.                         yield();
  1238.                         lck = NULL;
  1239.                     }
  1240.                     else
  1241.                         return r;
  1242.                 }
  1243.             }
  1244.         } while (!lck);
  1245.         lck->l = t.l;
  1246.         lck->l.l_pid = curproc->pid;
  1247.         lck->next = ti->locks;
  1248.         ti->locks = lck;
  1249.     /* mark the file as being locked */
  1250.         f->flags |= O_LOCK;
  1251.         return 0;
  1252.     }
  1253.     return EINVFN;
  1254. }
  1255.  
  1256. static long ARGS_ON_STACK 
  1257. tos_datime(f, timeptr, rwflag)
  1258.     FILEPTR *f;
  1259.     short *timeptr;
  1260.     int rwflag;
  1261. {
  1262.     if (rwflag) {
  1263.         struct tindex *ti = (struct tindex *)f->fc.index;
  1264.         ti->valid = 0;
  1265.     }
  1266.     return Fdatime(timeptr, (int)f->devinfo, rwflag);
  1267. }
  1268.  
  1269. static long ARGS_ON_STACK 
  1270. tos_close(f, pid)
  1271.     FILEPTR *f;
  1272.     int pid;
  1273. {
  1274.     LOCK *lck, **oldl;
  1275.     struct tindex *t;
  1276.     FILEPTR **old, *p;
  1277.     long r = 0;
  1278.     extern int flk;        /* set in main.c */
  1279.  
  1280.     t = (struct tindex *)(f->fc.index);
  1281. /* if this handle was locked, remove any locks held by the process
  1282.  */
  1283.     if (f->flags & O_LOCK) {
  1284.         TRACE(("tos_close: releasing locks (file mode: %x)", f->flags));
  1285.         oldl = &t->locks;
  1286.         lck = *oldl;
  1287.         while (lck) {
  1288.             if (lck->l.l_pid == pid) {
  1289.                 *oldl = lck->next;
  1290.                 if (flk)
  1291.                     (void)Flock((int)f->devinfo, 1,
  1292.                         lck->l.l_start, lck->l.l_len);
  1293.                 wake(IO_Q, (long)lck);
  1294.                 kfree(lck);
  1295.             } else {
  1296.                 oldl = &lck->next;
  1297.             }
  1298.             lck = *oldl;
  1299.         }
  1300.     }
  1301.  
  1302.     if (f->links <= 0) {
  1303. /* remove f from the list of open file pointers on this index */
  1304.         t->valid = 0;
  1305.         old = &t->open;
  1306.         p = t->open;
  1307.         while (p && p != f) {
  1308.             old = &p->next;
  1309.             p = p->next;
  1310.         }
  1311.         assert(p);
  1312.         *old = f->next;
  1313.         f->next = 0;
  1314.         r = Fclose((int)f->devinfo);
  1315.  
  1316. /* if the file was marked for deletion, delete it */
  1317.         if (!t->open) {
  1318.             if (t->attr & FA_DELETE) {
  1319.                 (void)Fdelete(t->name);
  1320.                 t->name = 0;
  1321.             }
  1322.         }
  1323.     }
  1324.     return r;
  1325. }
  1326.  
  1327. /*
  1328.  * check for disk change: called by the kernel if Mediach returns a
  1329.  * non-zero value
  1330.  */
  1331.  
  1332. long ARGS_ON_STACK 
  1333. tos_dskchng(drv)
  1334.     int drv;
  1335. {
  1336.     char dlet;
  1337.     int i;
  1338.     struct tindex *ti;
  1339.     FILEPTR *f, *nextf;
  1340.  
  1341.     dlet = 'A' + drv;
  1342. MEDIA_DB(("tos_dskchng(%c)", dlet));
  1343.     ti = gl_ti;
  1344.     for (i = 0; i < NUM_INDICES; i++, ti++) {
  1345.         if (ti->name && ti->name[0] == dlet) {
  1346. #ifdef NEWWAY
  1347.     /* only free the name if this index not used by any cookie */
  1348.             if (ti->links != 0)
  1349.                 ti->valid = 0;
  1350.             else
  1351. #endif /* NEWWAY */
  1352.             {
  1353.                 kfree(ti->name);
  1354.                 ti->name = 0;
  1355.             }
  1356.             if (!(ti->attr & FA_DIR)) {
  1357.                 nextf = ti->open;
  1358.                 while ( (f = nextf) != 0 ) {
  1359.                     nextf = f->next;
  1360.                     f->next = 0;
  1361.                 }
  1362.             }
  1363.             ti->open = 0;
  1364.     /* if there are any cookies pointing at this name, they're not
  1365.      * valid any more, so we will *want* to get an error if they're
  1366.      * used.
  1367.      */
  1368.         }
  1369.     }
  1370.     tmpindex.valid = 0;
  1371. /*
  1372.  * OK, make sure that GEMDOS knows to look for a change if we
  1373.  * ever use this drive again.
  1374.  */
  1375.     drvchanged[drv] = 1;
  1376.     return 1;
  1377. }
  1378.  
  1379. #ifdef NEWWAY
  1380. /* release/copy file cookies; these functions exist to keep
  1381.  * track of whether or not the kernel is still using a file
  1382.  */
  1383. long
  1384. tos_release(fc)
  1385.     fcookie *fc;
  1386. {
  1387.     struct tindex *ti = (struct tindex *)fc->index;
  1388.  
  1389.     if (ti->links <= 0) {
  1390.         FATAL("tos_release: link count of `%s' is %d", ti->name, ti->links);
  1391.     }
  1392.     ti->links--;
  1393.     COOKIE_DB(("tos_release: %s now has %d links", ti->name, ti->links));
  1394.     return 0;
  1395. }
  1396.  
  1397. long
  1398. tos_dupcookie(dest, src)
  1399.     fcookie *dest, *src;
  1400. {
  1401.     struct tindex *ti = (struct tindex *)src->index;
  1402.  
  1403.     if (ti->links <= 0) {
  1404.         FATAL("tos_dupcookie: link count of %s is %d", ti->name, ti->links);
  1405.     }
  1406.     ti->links++;
  1407.     COOKIE_DB(("tos_dupcookie: %s now has %d links", ti->name, ti->links));
  1408.     *dest = *src;
  1409.     return 0;
  1410. }
  1411. #endif
  1412.  
  1413. /*
  1414.  * utility function: sets the TOS DTA, and also records what directory
  1415.  * this was in. This is just to save us a call into the kernel if the
  1416.  * correct DTA has already been set.
  1417.  */
  1418.  
  1419. static void
  1420. do_setdta(dta)
  1421.     DTABUF *dta;
  1422. {
  1423.     if (dta != lastdta) {
  1424.         Fsetdta(dta);
  1425.         lastdta = dta;
  1426.     }
  1427. }
  1428.  
  1429. /*
  1430.  * routines for forcing a media change on drive "drv"
  1431.  */
  1432.  
  1433. static int chdrv;
  1434.  
  1435. /* new Getbpb function: when this is called, all the other
  1436.  * vectors can be un-installed
  1437.  */
  1438.  
  1439. static long ARGS_ON_STACK (*Oldgetbpb) P_((int));
  1440. static long ARGS_ON_STACK (*Oldmediach) P_((int));
  1441. static long ARGS_ON_STACK (*Oldrwabs) P_((int, void *, int, int, int, long));
  1442.  
  1443. static long  ARGS_ON_STACK
  1444. Newgetbpb(d)
  1445.     int d;
  1446. {
  1447.     if (d == chdrv) {
  1448. MEDIA_DB(("Newgetbpb(%c)", d+'A'));
  1449. if (Oldgetbpb == Newgetbpb) {
  1450. MEDIA_DB(("AARGH!!! BAD BPBs"));
  1451. }
  1452.         *((Func *)0x472L) = Oldgetbpb;
  1453.         *((Func *)0x476L) = Oldrwabs;
  1454.         *((Func *)0x47eL) = Oldmediach;
  1455.     }
  1456.     return (*Oldgetbpb)(d);
  1457. }
  1458.  
  1459. static long ARGS_ON_STACK
  1460. Newmediach(d)
  1461.     int d;
  1462. {
  1463.     if (d == chdrv) {
  1464. MEDIA_DB(("Newmediach(%c)", d+'A'));
  1465.         return 2;
  1466.     }
  1467.     return (*Oldmediach)(d);
  1468. }
  1469.  
  1470. static long ARGS_ON_STACK
  1471. Newrwabs(d, buf, a, b, c, l)
  1472.     int d;
  1473.     void *buf;
  1474.     int a, b, c;
  1475.     long l;
  1476. {
  1477.     if (d == chdrv) {
  1478. MEDIA_DB(("Newrwabs"));
  1479.         return E_CHNG;
  1480.     }
  1481.     return (*Oldrwabs)(d, buf, a, b, c, l);
  1482. }
  1483.  
  1484. static void
  1485. force_mediach(d)
  1486.     int d;
  1487. {
  1488. #ifdef FSFIRST_MEDIACH
  1489.     static char fname[] = "X:\\*.*";
  1490. #else
  1491.     long r;
  1492.     static char fname[] = "X:\\X";
  1493. #endif
  1494.     TRACE(("tosfs: disk change drive %c", d+'A'));
  1495. MEDIA_DB(("forcing media change on %c", d+'A'));
  1496.  
  1497.     chdrv = d;
  1498.     Oldrwabs = *((Func *)0x476L);
  1499.     Oldgetbpb = *((Func *)0x472L);
  1500.     Oldmediach = *((Func *)0x47eL);
  1501.  
  1502.     if (Oldrwabs == Newrwabs || Oldgetbpb == Newgetbpb ||
  1503.         Oldmediach == Newmediach) {
  1504.         FORCE("tosfs: error in media change code");
  1505.     } else {
  1506.         *((Func *)0x476L) = Newrwabs;
  1507.         *((Func *)0x472L) = Newgetbpb;
  1508.         *((Func *)0x47eL) = Newmediach;
  1509.     }
  1510.  
  1511.     fname[0] = d + 'A';
  1512. MEDIA_DB(("calling GEMDOS"));
  1513. #ifdef FSFIRST_MEDIACH
  1514.     (void)Fsfirst(fname, 8);
  1515. #else    
  1516.     r = Fopen(fname, 0);
  1517.     if (r >= 0) Fclose((int)r);
  1518. #endif
  1519. MEDIA_DB(("done calling GEMDOS"));
  1520.     drvchanged[d] = 0;
  1521.     if ( *((Func *)0x476L) == Newrwabs ) {
  1522.         DEBUG(("WARNING: media change not performed correctly"));
  1523.         *((Func *)0x472L) = Oldgetbpb;
  1524.         *((Func *)0x476L) = Oldrwabs;
  1525.         *((Func *)0x47eL) = Oldmediach;
  1526.     }
  1527. }
  1528.